﻿using System;
using Selenium;
using SeleniumToolkit.Core;

namespace SeleniumToolkit
{
    /// <summary>
    /// Context object for Selenium Browser
    /// </summary>
    public class Browser : IDisposable
    {
        #region Static Members
        /// <summary>
        /// Current selenium instance for this thread
        /// </summary>
        public static ISelenium Current
        {
            get
            {
                if (_instance == null) return null;

                return _instance.Selenium;
            }
        }

        /// <summary>
        /// Provides details about the current Browser
        /// </summary>
        public static Browser BrowserInfo
        {
            get { return _instance; }
        }

        [ThreadStatic]
        private static Browser _instance;
        #endregion

        #region Instance Members

        #region Constructor / Finalizer
        /// <summary>
        /// Constructs a Selenium wrapper for the context
        /// </summary>
        /// <param name="sessionInfo">Session settings</param>
        public Browser(ISessionInfo sessionInfo)
        {
            _sessionInfo = sessionInfo;

            // if the fixture/test is responsible for starting/stopping the selenium session,
            // ensure the selenium session is started and the session is promoted to the context level
            if (SessionOwner)
            {
                // configure selenium object for test
                _selenium = SeleniumFactory.Create(_sessionInfo.DefaultBrowser, _sessionInfo.BaseUrl);
                _selenium.Start();
                _selenium.SetTimeout(_sessionInfo.Timeout);

                // set static instance (Browser.Current)
                _instance = this;
            }
        }


        /// <summary>
        /// Finalizer
        /// </summary>
        ~Browser()
        {
            Dispose(false);
        } 
        #endregion

        /// <summary>
        /// Selenium instance
        /// </summary>
        protected ISelenium Selenium
        {
            get
            {
                // could happen in user framework code
                if (_disposed)
                    throw new NotSupportedException("You cannot access selenium instance after it has been disposed.");

                // catch errors in the addin where selenium may be used accidentally
                // Q: using Browser.Current has the same error?
                if (!SessionOwner)
                    throw new InvalidOperationException("You are attempting to use a selenium instance that was not defined as the session owner.");

                return _selenium;
            }
        }

        /// <summary>
        /// Indicates whether this Browser instance was created as the active selenium session owner
        /// </summary>
        public bool SessionOwner
        {
            get { return _sessionInfo.SessionOwner; }
        }

        /// <summary>
        /// The current selenium browser string
        /// </summary>
        public string BrowserString
        {
            get { return _sessionInfo.DefaultBrowser; }
        }

        /// <summary>
        /// The base url of the selenium session
        /// </summary>
        public string BaseUrl
        {
            get { return _sessionInfo.BaseUrl; }
        }

        /// <summary>
        /// The timeout of the selenium session
        /// </summary>
        public string Timeout
        {
            get { return _sessionInfo.Timeout; }
        }

        #region IDisposable Members

        /// <summary>
        /// Dipose manage resources
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }

        /// <summary>
        /// Dispose pattern (clean up managed resources)
        /// </summary>
        /// <param name="disposing"></param>
        protected void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                // prevent finalizer from being called later if we are properly disposing now
                if (disposing)
                {
                    GC.SuppressFinalize(this);
                }               

                // if this is the session owner
                // close the session and remove the static instance
                if (SessionOwner)
                {
                    try
                    {
                        // instance should always be available
                        if (_instance != null)
                            _instance.Selenium.Stop();
                    }
                    finally
                    {
                        _instance = null;
                        _selenium = null;
                        _disposed = true;
                    }
                }

                _disposed = true; // prevent finalizer for non-session holders
            }
        }

        #endregion

        private bool _disposed;
        private readonly ISessionInfo _sessionInfo;
        private ISelenium _selenium;
        #endregion
    }
}
